// Interfaces of Eutran Module
// should call Eutran function through class EutranManager from outside
#ifndef EUTRAN_MANAGER_H
#define EUTRAN_MANAGER_H

#include <vector>
#include <stdint.h>
#include <arpa/inet.h>
#include <string>
#include <map>
#include "../logServer/logger.h"
#include "syscore.h"
#include "apis.h"

inline const char* shortname(const char *filename)
{
   return strrchr(filename,'/') != 0 ? strrchr(filename,'/')+1 : filename;
}

#define EMLogDebug(fmt, args...) LogDebug(MODULE_EUTRAN_SNIFFER, fmt, ##args);
//#define LogInfo(fmt, args...) MOD_log(MODULE_EUTRAN_SNIFFER, LOG_INFO, "%s:%d "fmt, shortname(__FILE__), __LINE__, ##args);
//#define EMLogError(fmt, args...) MOD_log(MODULE_EUTRAN_SNIFFER, LOG_ERR, "%s:%d "fmt, shortname(__FILE__), __LINE__, ##args);
//#define EMLogNotice(fmt, args...) MOD_log(MODULE_EUTRAN_SNIFFER, LOG_NOTICE, "%s:%d "fmt, shortname(__FILE__), __LINE__, ##args);
//#define EMLogNotice(fmt, args...) dbgprintf(DEBUG_INFO,stdout,"%s:%d ", shortname(__FILE__), __LINE__, ##args);
//#define EMLogWarning(fmt, args...) MOD_log(MODULE_EUTRAN_SNIFFER, LOG_WARNING, "%s:%d "fmt, shortname(__FILE__), __LINE__, ##args);
#define EMLogInfo(fmt, args...) LogInfo(MODULE_EUTRAN_SNIFFER, fmt, ##args);
#define EMLogError(fmt, args...) LogError(MODULE_EUTRAN_SNIFFER, fmt, ##args);
#define EMLogWarning(fmt, args...) LogWarning(MODULE_EUTRAN_SNIFFER, fmt, ##args);
#define EMLogNotice(fmt, args...) LogNotice(MODULE_EUTRAN_SNIFFER, fmt, ##args);

class EutranTimerElementInterface
{
public:
    EutranTimerElementInterface():isQueue_(false){};

    virtual ~EutranTimerElementInterface(){};
    
    virtual void callback() = 0;

    bool isQueue_;
};

template<class T>
class EutranTimerElement : public EutranTimerElementInterface
{
public:
    typedef void (T::*Hook)();

    EutranTimerElement(T* obj, Hook h):
    obj_(obj),
    hook_(h)
    {};

    virtual ~EutranTimerElement()
    {
    };
    
    virtual void callback()
    {
        (obj_->*hook_)();
    }
private:
    T* obj_;
    Hook hook_;
};

class EutranTimer
{
public:
    static EutranTimer* instance();

    bool initialize();
    bool queue(uint32_t timerVal, EutranTimerElementInterface* element);
    bool dequeue(EutranTimerElementInterface* element);

private:
    friend class EutranMgr;
    
    EutranTimer();
    EutranTimer(const EutranTimer&);
    EutranTimer& operator=(const EutranTimer&);

    void handleTick();

    typedef std::map<EutranTimerElementInterface*, uint32_t> TimerElementType;
    TimerElementType elements_;
};

typedef std::pair<uint16_t, uint16_t> EarfcnRangeType;

#define EutranManagerPtr (EutranManager::instance())
#define EutranMgrPtr (EutranMgr::instance())

class WrapperCmdPara
{
public:
    WrapperCmdPara();
    WrapperCmdPara(uint32_t type, uint32_t length, uint8_t* value);
    WrapperCmdPara(uint32_t type, uint8_t value);
    WrapperCmdPara(uint32_t type, int8_t value);
    WrapperCmdPara(uint32_t type, uint16_t value);
    WrapperCmdPara(uint32_t type, uint32_t value);
    WrapperCmdPara(uint32_t type, int32_t value);
    WrapperCmdPara(uint32_t type, uint64_t value);
    uint32_t paraType_;
    uint32_t paraLength_;
    uint8_t* value_;

    union
    {
        uint8_t v8;
        int8_t iv8;
        uint16_t v16;
        uint32_t v32;
        int32_t i32;
        uint64_t v64;
    } innerValue;
    
private:
};

class ReliableMessageElement
{
public:
    ReliableMessageElement(uint8_t* buf, uint32_t length, uint32_t seqNo, uint8_t slotId);
    ~ReliableMessageElement();

    void getMsgBuffer(uint8_t*& buf, uint32_t& length);
    uint32_t& getTickRef();
    uint32_t getSeqNo();
    uint8_t getSlotId();

private:
    ReliableMessageElement(const ReliableMessageElement&);
    ReliableMessageElement& operator=(const ReliableMessageElement&);

    uint8_t* buf_;
    uint32_t length_;
    uint32_t tick_;
    uint32_t seqNo_;
    uint8_t slotId_;
};

class EutranMgr
{
public:
    static EutranMgr* instance();
    bool initialize();

    bool setBbuIp(uint8_t slotId, std::string& ipAddr, uint16_t port);
    bool setTxPowerDb(uint8_t slotId, int8_t txPwerDb);
    bool setRxPower(uint8_t slotId, uint8_t rxPwer);

    inline MODREG* getModRegInfo()
    {
        return &modEutranmgrInfo_;
    }

    void cfgRtParameter(PPARAM pParam, uint32_t len);
    void processStdTxPowerDbGetMessage(PPARAM pParam, uint32_t len);
    void processStdTxPowerGetMessage(PPARAM pParam, uint32_t len);
    void processStdTxPowerCfgMessage(PPARAM pParam, uint32_t len);
    void processBbuMessage(uint8_t* data, uint32_t dataLength, in_addr_t peerAddr);
    void processPmRestart();
    void setSnifferResultReportStrategy(uint8_t strategy);
    void setAutoCfgCellFlag(bool);
    void initConfigComp(uint8_t slotId);

    void saveMsgCmdCodeAndSrcID(const PMSGHEADER srcID, PCMDHEADER hdr);
    bool isProcessingCmd();
    void reportInfo(uint16_t cmdCode, const std::vector<WrapperCmdPara>& paraArray);
    void reportEvent(uint16_t cmdCode, const std::vector<WrapperCmdPara>& paraArray);
    void reportUeId(uint16_t cmdCode, const std::vector<WrapperCmdPara>& paraArray);
    void reportSnifferResult(uint16_t cmdCode, const std::vector<WrapperCmdPara>& paraArray);
    void reportScanResult(uint16_t cmdCode, const std::vector<WrapperCmdPara>& paraArray);
    void responseSuccess();
    void responseFailure();
    void responseToSrcModule(uint16_t cmdType, const std::vector<WrapperCmdPara>& paraArray);
    void responseToModule(uint16_t dstId, uint16_t msgType, uint16_t cmdType, uint16_t cmdCode, uint16_t cmdIndex, const std::vector<WrapperCmdPara>& paraArray);

    void startSnifferManual(PPARAM pParam, uint32_t len);
    void reqGpsState(PPARAM pParam, uint32_t len);
    void reportSnifferResult(PPARAM pParam, uint32_t len);
    void locationModeSetting(PPARAM pParam, uint32_t len);
    void BlackListModeSetting(PPARAM pParam, uint32_t len);
    void WorkPeriodSetting(PPARAM pParam, uint32_t len);
    void cfgAutoSniffer(PPARAM pParam, uint32_t len);
    void ueIdFilter(PPARAM pParam, uint32_t len);
    void scanLte(PPARAM pParam, uint32_t len);
    void enableInterference(PPARAM pParam, uint32_t len);
    void startSnifferAuto(const std::vector<uint8_t>& bandList,
                              const std::vector< std::pair<uint16_t/*lowerEarfcn*/, uint16_t/*upperEarfcn*/> >& earfcnRange, 
                              const std::vector<uint16_t>& pciList);
    void stopSniffer();
    void resetSniffer();
    bool configCell(uint8_t slotId, uint16_t dlArfcn, uint16_t ulArfcn, uint16_t pci, uint16_t tac, 
                    std::vector<uint32_t>& plmnList, 
                    const std::vector<uint16_t>& intraFreqPci, const std::vector<uint16_t>& interFreq,
                    int16_t* txPwer, bool  isMeasEnable, uint8_t snifferType);
    bool UpdateCell(uint8_t slotId, std::vector<uint32_t>* plmnList, uint16_t* tac, uint16_t* earfcn, bool* measEnablePtr, bool isChangeOperator);
    void requestSnifferStatus(uint8_t slotId=0);
    void requestSoftwareVersion(uint8_t slotId);
    void requestBbuState(uint8_t slotId);
    void rebootBbu(uint8_t slotId, std::string& desc);
    void setSyncMode(uint8_t slotId, uint8_t statusCode);
    void getSyncState(uint8_t slotId);
    void upgradeBbu(uint8_t slotId, std::string& ipAddr, std::string& fileName, std::string& md5, std::string& userName, std::string& passwd, uint8_t downMode);
    void handleTimerTick();

    void processShowBbuCommand();
    void processUeRedirectCommand(PPARAM pParam, uint32_t len);
    void processScanResult(uint8_t slotId, uint8_t* data, uint32_t length);
    void bbuPowerReset(uint8_t slotId);
    void setBbuConfiged(uint8_t slotId);
    void setThreadStat(bool IsOk);

	void setScanMode(PPARAM pParam, uint32_t len);
	void setBbuSnfArfcn(uint8_t slotId, std::string& arfcnPara);

    void changBbuOperator(int32_t OperatorType ,uint8_t slotId);
    void changTeleOperator(PPARAM pParam, uint32_t len);

    void locationEnableSetting(PPARAM pParam, uint32_t len);

	void setPlmnIdList(uint8_t slotId, std::vector<uint32_t> &plmnList);

    void reportUsingEarfcn(uint32_t slotId, uint16_t earfcn);

    void controlPA(uint8_t paId, bool on);
    void handlePaOnResponse(PPARAM pParam, uint32_t len);
    void handlePaOffResponse(PPARAM pParam, uint32_t len);



private:
    EutranMgr();
    EutranMgr(const EutranMgr&);
    EutranMgr& operator=(const EutranMgr&);
    pthread_mutex_t mutex;
    bool getRcvThreadStat();
    bool rcvThreadOk;

    void reportToModule(uint16_t dstId, uint16_t cmdCode, const std::vector<WrapperCmdPara>& paraArray); 

    static MODREG modEutranmgrInfo_;
    CMDHEADER processingCmdHdr_;
    uint16_t processingMsgSrcId_;
    uint16_t processingMsgTypeId_;

    void generageArfcnRange(std::vector<EarfcnRangeType>& range, const std::vector<uint8_t>& bandList, const std::vector<EarfcnRangeType>& earfcnRange);
    void processUeId(uint8_t slotId, uint8_t* data, uint32_t length);
    
    uint32_t cmdTimeoutVal_;
    uint32_t heartBeatVal_;
    EutranTimerElement<EutranMgr> cmdTimeoutHandler_;
    EutranTimerElement<EutranMgr> TimerHeartBeat_;
    void handleCmdProcessTimeout();
    void timerHeartBeat();
    void splitImsi(std::string& imsiStr, std::vector<std::string>& imsiList);

    bool parseTime(std::string para, uint8_t& hour, uint8_t& min);
    
    bool convertToArfcnList(const char* paraValue, uint32_t paraLength, std::vector<uint16_t>& arfcnRangeList);
};
#endif
